package com.chengqs.summer.utils;

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.resolver.Resolver;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 实用工具类，用于解析 YAML 文件。
 *
 * 使用 SnakeYAML 库来处理 YAML 数据，提供了从类路径中读取、解析 YAML 文件，并将其内容转换为 Java 的 Map 结构的功能。
 *
 * 该类应用了以下设计模式：
 *
 * <h2>1. 单例模式（Singleton Pattern）(创建)</h2>
 * <p>
 * 本类作为工具类，通过静态方法提供服务，不需要创建实例。所有方法都是静态的，符合单例模式的特性，
 * 实现了全局唯一访问点的效果。
 * </p>
 *
 * <h2>2. 外观模式（Facade Pattern）(结构)</h2>
 * <p>
 * 外观模式用于封装复杂的 YAML 解析过程，提供了简单易用的接口来加载和解析 YAML 文件。用户无需了解
 * SnakeYAML 的具体实现细节，只需使用类中提供的静态方法即可完成 YAML 的加载和解析。
 * </p>
 *
 * <h2>3. 模板方法模式（Template Method Pattern）(行为)</h2>
 * <p>
 * 模板方法模式定义了将嵌套的 YAML 数据结构转换为扁平化 Map 的算法骨架。`convertTo` 方法递归地处理
 * 嵌套结构，允许在不改变算法结构的情况下重新定义某些步骤，使得转换逻辑具有灵活的可扩展性。
 * </p>
 *
 * <h2>4. 装饰器模式（Decorator Pattern）(结构)</h2>
 * <p>
 * 装饰器模式通过 `NoImplicitResolver` 类对 `Resolver` 进行装饰，以禁用默认的隐式转换，将所有 YAML 数据
 * 视为字符串。这种设计允许在不修改原有类的基础上，动态地添加新的功能或行为。
 * </p>
 */
@SuppressWarnings("unused")
public class YamlUtils {

    /**
     * 从给定的类路径加载 YAML 文件并将其解析为一个 Map<String, Object>。
     * @param path YAML 文件在类路径中的位置。
     * @return 返回一个 Map<String, Object>，表示解析后的 YAML 数据。
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> loadYaml(String path) {
        // LoaderOptions、DumperOptions、Representer 和 Resolver 是 SnakeYAML 中的配置类，用于自定义 YAML 的加载和表示行为。
        LoaderOptions loaderOptions = new LoaderOptions();
        DumperOptions dumperOptions = new DumperOptions();
        Representer representer = new Representer(dumperOptions);
        // NoImplicitResolver 用于禁用所有的隐式转换，将 YAML 文件中的所有值都视为字符串。
        NoImplicitResolver resolver = new NoImplicitResolver();
        Yaml yaml = new Yaml(new Constructor(loaderOptions), representer, dumperOptions, loaderOptions, resolver);
        // 类路径中读取 YAML 文件
        return ClassPathUtils.readInputStream(path, (input) -> {
            // yaml.load(input) 解析输入流中的 YAML 数据，并返回解析后的 Map。
            return (Map<String, Object>) yaml.load(input);
        });
    }

    /**
     * 加载 YAML 文件并将其转换为一个扁平化的 Map。
     * @param path YAML 文件在类路径中的位置。
     * @return 返回一个 Map<String, Object>，表示扁平化后的 YAML 数据。
     */
    public static Map<String, Object> loadYamlAsPlainMap(String path) {
        // 调用 loadYaml 方法来加载 YAML 数据。
        Map<String, Object> data = loadYaml(path);
        // 创建一个新的 LinkedHashMap 来存储扁平化的键值对。
        Map<String, Object> plain = new LinkedHashMap<>();
        // 使用 convertTo 方法递归地将嵌套的 YAML 数据转换为简单的键值对。
        convertTo(data, "", plain);
        return plain;
    }

    /**
     * 递归地将嵌套的 YAML 数据结构转换为扁平化的 Map。
     * @param source 源数据，表示需要转换的嵌套结构。
     * @param prefix 前缀，用于构建扁平化的键名。
     * @param plain 扁平化后的 Map，存储结果。
     */
    static void convertTo(Map<String, Object> source, String prefix, Map<String, Object> plain) {
        for (String key : source.keySet()) {
            // 遍历源 Map 的每个键值对。
            Object value = source.get(key);
            if (value instanceof Map) {
                // 如果值是一个 Map，则递归调用 convertTo 方法，构建新的前缀（prefix + key + "."）
                @SuppressWarnings("unchecked")
                Map<String, Object> subMap = (Map<String, Object>) value;
                convertTo(subMap, prefix + key + ".", plain);
            } else if (value instanceof List) {
                // 如果值是一个 List，直接将其存入 plain Map
                plain.put(prefix + key, value);
            } else {
                // 否则，将值转换为字符串并存入 plain Map
                plain.put(prefix + key, value.toString());
            }
        }
    }
}

/**
 * 禁用 SnakeYAML 的隐式类型转换，将所有 YAML 数据视为字符串。
 * 继承自 Resolver，并在构造函数中清除了所有的隐式解析器。
 * 这意味着 YAML 中的布尔值、数字等将不会被自动转换，而是保留其原始字符串形式。
 */
class NoImplicitResolver extends Resolver {

    public NoImplicitResolver() {
        super();
        super.yamlImplicitResolvers.clear();
    }
}
